package model

/*******************************************************************************
Copyright:cloud
Author:cloudapex@126.com
Version:1.0
Date:2020-07-07
Description: 用户组模型
*******************************************************************************/
import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"gitee.com/cloudapex/ulern/util"
	"xorm.io/xorm"
)

// => 用户组表
type Group struct {
	Id       int64     `json:"id"`
	ParentId int64     `json:"parentid" xorm:"notnull index"`        // 父Id
	NodePath string    `json:"-" xorm:"varchar(64) notnull"`         // 节点路径
	Icon     string    `json:"icon" xorm:"varchar(128)"`             // 图标
	Name     string    `json:"name" xorm:"varchar(64) notnull"`      // 组名
	Tntyp    ETreeNode `json:"tntyp" xorm:"notnull default 1"`       // 用户组类型(1:逻辑组;2:实体组)
	Order    int       `json:"order" xorm:"notnull default 0 index"` // 排序值
	State    int       `json:"state" xorm:"notnull default 1"`       // 状态(1:正常;2:失效)

	CreatedAt util.TJsTime `json:"at" xorm:"created"`
	UpdatedAt util.TJsTime `json:"-" xorm:"updated"`

	CInIds []int64 `json:"-" xorm:"-"`
	Childs []Group `json:"children" xorm:"-"`
}

// => 组织树[基本]
type GroupLable struct {
	Id     int64        `json:"id"`       // 用户组Id
	Name   string       `json:"name"`     // 用户组名
	Tntyp  ETreeNode    `json:"tntyp"`    // 用户组类型
	Childs []GroupLable `json:"children"` // 子节点
}

// => 组织列表(全路径)
type GroupChain struct {
	Id      int64   `json:"id"`
	LName   string  `json:"lname"` // 链名称
	PathIds []int64 `json:"-"`     // 路径上的Ids
}

// => 用户组与角色关联表
type GroupRole struct {
	Id        int64
	GroupId   int64        `xorm:"notnull index"` // 用户组Id
	RoleId    int64        `xorm:"notnull index"` // 角色Id
	CreatedAt util.TJsTime `xorm:"created"`
}

// ----- Group
func (this *Group) Load() (bool, error) {
	return MDB(EMDB_Permt).Get(this)
}
func (this *Group) Create() (int64, error) {
	return MDB(EMDB_Permt).Insert(this)
}
func (this *Group) Update(id int64, columns ...string) (int64, error) {
	return MDB(EMDB_Permt).ID(id).MustCols(columns...).Update(this)
}
func (this *Group) Delete(id int64) (int64, error) {
	return MDB(EMDB_Permt).ID(id).Delete(&Group{})
}
func (this *Group) condis(q *xorm.Session, tbl string) *xorm.Session {
	util.Cast(tbl != "", func() { tbl += "." }, nil)

	if this.Name != "" {
		q = q.Where(fmt.Sprintf("%sname like ?", tbl), "%"+this.Name+"%")
	}
	if this.Tntyp != 0 {
		q = q.Where(fmt.Sprintf("%stntyp = ?", tbl), this.Tntyp)
	}
	if this.State > 0 {
		q = q.Where(fmt.Sprintf("%sstate = ?", tbl), this.State)
	}
	if len(this.CInIds) > 0 {
		q = q.In(fmt.Sprintf("%sid", tbl), this.CInIds)
	}
	return q
}
func (this *Group) condList() bool {
	return this.Name != ""
}
func (this *Group) VerifyIds(ids []int64) (bool, error) {
	if len(ids) == 0 {
		return true, nil
	}
	c, err := MDB(EMDB_Permt).Table(&Group{}).In("id", ids).Count()
	return c == int64(len(ids)), err
}
func (this *Group) Find() ([]Group, error) {
	sl, dl := []*Group{}, []Group{}

	q := MDB(EMDB_Permt).Table(&Group{})
	q = this.condis(q, "")

	// 按普通列表返回
	if this.condList() {
		if err := q.Asc("order").Find(&dl); err != nil {
			return nil, err
		}
		return dl, nil
	}

	// 按树形列表返回
	if err := q.Asc("order").Find(&sl); err != nil {
		return nil, err
	}

	for _, it := range sl {
		if it.ParentId != 0 {
			continue
		}
		dl = append(dl, nestGroup(sl, *it))
	}
	return dl, nil
}
func (this *Group) Findm() (map[int64]*Group, error) {
	dl := map[int64]*Group{}

	q := MDB(EMDB_Permt).Table(&Group{})
	q = this.condis(q, "")

	if err := q.Asc("order").Find(&dl); err != nil {
		return nil, err
	}
	return dl, nil
}
func (this *Group) FindTree() ([]GroupLable, error) {
	sl := []*Group{}

	q := MDB(EMDB_Permt).Table(&Group{})
	q = this.condis(q, "")
	if err := q.Asc("order").Find(&sl); err != nil {
		return nil, err
	}

	dl := []GroupLable{}
	for _, it := range sl {
		if it.ParentId != 0 {
			continue
		}
		dl = append(dl, nestGroupLable(sl, GroupLable{it.Id, it.Name, it.Tntyp, nil}))
	}

	return dl, nil
}

// ----- internal
func nestGroup(sl []*Group, grup Group) Group {
	if grup.Tntyp == ETreeNode_Leaf {
		return grup
	}
	for _, it := range sl {
		if grup.Id != it.ParentId {
			continue
		}
		grup.Childs = append(grup.Childs, nestGroup(sl, *it))
	}
	return grup
}
func nestGroupLable(sl []*Group, grup GroupLable) GroupLable {
	if grup.Tntyp == ETreeNode_Leaf {
		return grup
	}
	for _, it := range sl {
		if grup.Id != it.ParentId {
			continue
		}
		grup.Childs = append(grup.Childs, nestGroupLable(sl, GroupLable{it.Id, it.Name, it.Tntyp, nil}))
	}
	return grup
}

// ----- GroupRole
func (this *GroupRole) Insert(gId int64, rIds []int64) error {
	if len(rIds) == 0 {
		return nil
	}

	items := []GroupRole{}
	for _, rId := range rIds {
		items = append(items, GroupRole{GroupId: gId, RoleId: rId})
	}
	for n := 0; ; n++ {
		end := (n + 1) * DEF_INSERT_BATCH_SIZE
		if end >= len(items) {
			end -= (end - len(items))
		}

		_, err := MDB(EMDB_Permt).Table(this).Insert(items[n*DEF_INSERT_BATCH_SIZE : end])
		if err != nil {
			return err
		}

		if end >= len(items) {
			break
		}
	}
	return nil
}
func (this *GroupRole) Delete(gId, rId int64) (int64, error) {
	if gId == 0 && rId == 0 {
		return 0, errors.New("gId == 0 && rId == 0")
	}

	q := MDB(EMDB_Permt).Table(this)

	util.Cast(gId != 0, func() { q = q.Where("group_id=?", gId) }, nil)
	util.Cast(rId != 0, func() { q = q.Where("role_id=?", rId) }, nil)

	return q.Delete(&GroupRole{})
}
func (this *GroupRole) condis(q *xorm.Session, tbl string) *xorm.Session {
	util.Cast(tbl != "", func() { tbl += "." }, nil)

	if this.GroupId != 0 {
		q.Where(fmt.Sprintf("%sgroup_id = ?", tbl), this.GroupId)
	}
	if this.RoleId != 0 {
		q.Where(fmt.Sprintf("%srole_id = ?", tbl), this.RoleId)
	}
	return q
}
func (this *GroupRole) UpdGroup(old, new int64) (int64, error) {
	if old <= 0 {
		return 0, errors.New("old <= 0")
	}
	return MDB(EMDB_Permt).Update(&GroupRole{GroupId: new}, &GroupRole{GroupId: old})
}
func (this *GroupRole) FindIds() ([]int64, error) {
	ids := []int64{}

	q := MDB(EMDB_Permt).Table(this)

	util.Cast(this.GroupId != 0, func() { q = q.Cols("role_id") }, nil)
	util.Cast(this.RoleId != 0, func() { q = q.Cols("group_id") }, nil)

	return ids, this.condis(q, "").Find(&ids)
}

// ----- UserGroupWithGroup
type UserGroupWithGroup struct {
	UserGroup `xorm:"extends"`
	Group     `xorm:"extends"`
}

func (this *UserGroupWithGroup) FindChains() ([]GroupChain, error) {
	dl := []GroupChain{}

	sl := []*Group{}

	q := MDB(EMDB_Permt).Table(&UserGroup{}).Select("`group`.*")
	q = q.Join("INNER", "group", "user_group.group_id = group.id")

	q = this.UserGroup.condis(q, "user_group")
	q = this.Group.condis(q, "group")

	if err := q.GroupBy("group.id").Asc("group.parent_id", "group.order").Find(&sl); err != nil {
		return dl, err
	}

	aIds := []int64{}
	for n, it := range sl {
		dl = append(dl, GroupChain{Id: it.Id, LName: it.Name})
		json.Unmarshal([]byte("["+strings.Join(strings.Split(it.NodePath, "/"), ",")+"]"), &dl[n].PathIds)
		aIds = append(aIds, dl[n].PathIds...)
	}

	aIds = util.RemoveDuplicate64(aIds)

	groups, err := (&Group{CInIds: aIds}).Findm()
	if err != nil {
		return nil, err
	}

	for i, _ := range dl {
		names := []string{}
		for _, id := range dl[i].PathIds {
			if it, ok := groups[id]; ok {
				names = append(names, it.Name)
			}
		}
		names = append(names, dl[i].LName)
		dl[i].LName = strings.Join(names, "-")
	}
	return dl, nil
}
